package org.capybara.coffeehouse.insteon;

import java.util.ArrayList;
import java.util.List;

import org.capybara.coffeehouse.insteon.InsteonMessage.MessageType;

public class PlmMessage {

	
	private PlmMessageCode code;
	private List<Integer> data;
	
	public enum PlmMessageCode {
		
		STANDARD_MESSAGE_RX(0x50,9),
		EXTENDED_MESSAGE_RX(0x51,23),
		X10_RX(0x52,2),
		ALL_LINKING_COMPLETED(0x53,8),
		BUTTON_EVENT_REPORT(0x54,1),
		USER_RESET_DETECTED(0x55,0),
		ALL_LINK_CLEANUP_FAILURE_REPORT(0x56,5),
		ALL_LINK_RECORD_RESPONSE(0x57,8),
		ALL_LINK_CLEANUP_STATUS_REPORT(0x58,1),
		
		GET_IM_INFO(0x60,7),
		SEND_ALL_LINK_COMMAND(0x61,4),

/*
 * These two messages are a weird special case
 * because there is a variable length for the same
 * message code.  We shouldn't need to parse these here
 * anyways, really.
 *		STANDARD_MESSAGE_TX(0x62,7),
 *		EXTENDED_MESSAGE_TX(0x62,21),
 */
		
		SEND_X10(0x63,2),
		START_ALL_LINKING(0x64,4),
		CANCEL_ALL_LINKING(0x65,1),
		SET_HOST_DEVCAT(0x66,4),
		RESET_IM(0x67,1),
		SET_ACK_MESSAGE_BYTE(0x68,2),
		GET_FIRST_ALL_LINK_RECORD(0x69,1),
		GET_NEXT_ALL_LINK_RECORD(0x6A,1),
		SET_IM_CONFIGURATION(0x6B,2),
		GET_ALL_LINK_RECORD_FOR_SENDER(0x6C,1),
		LED_ON(0x6D,1),
		LED_OFF(0x6E,1),
		MANAGE_ALL_LINK_RECORD(0x6F,10),
		SET_INSTEON_NAK_MESSAGE_BYTE(0x70,2),
		SET_INSTEON_ACK_MESSAGE_TWO_BYTES(0x71,3),
		RF_SLEEP(0x72,1),
		GET_IM_CONFIGURATION(0x73,4);
		
		int id;
		int length;

		PlmMessageCode(int id, int length) {
			this.id = id;
			this.length = length;
		}
		
		public int getLength() {
			return length;
		}
	
		public static PlmMessageCode valueOfId(int id) {
			for (PlmMessageCode message : values()) {
				if (message.id == id) {
					return message;
				} 
			}
			throw new IllegalArgumentException("no enum for id "+id);
		}
	}
	
	
	public PlmMessage(int code) {
		this.code = PlmMessageCode.valueOfId(code);
	}
	
	public PlmMessage(byte code) {
		this.code = PlmMessageCode.valueOfId(code & 0xFF);
	}
	
	public String getName() {
		return code.name();
	}
	
	public PlmMessage(PlmMessageCode code) {
		this.code = code;
	}
	
	public int getLength() {
		return code.getLength();
	}
	
	public void setData(List<Integer> data) {
		this.data = new ArrayList<Integer>(data);
	}
	
	public void setData(byte[] data) {
		this.data = new ArrayList<Integer>();
		for (int i = 0; i < data.length; i++) {
			this.data.add(new Integer(data[i] & 0xFF));		
		}
	}
	
	public void setData(int[] data) {
		this.data = new ArrayList<Integer>();
		for (int i = 0; i < data.length; i++) {
			this.data.add(new Integer(data[i]));		
		}
	}

	public List<Integer> getData() {
		return new ArrayList<Integer>(data);
	}
	
	public int getDataByte(int i) {
		return data.get(i);
	}
	
	public void setDataByte(int index, int dataByte) {
		data.set(index,dataByte);
	}
	
	public void setDataByte(int index, byte dataByte) {
		data.set(index,new Integer(dataByte & 0xFF));
	}
	
	public PlmMessageCode getCode() {
		return code;
	}
	
	public String toString() {
		return code.name()+": "+Util.getHexString(data);
	}
	
	/** 
	 * Returns the insteon message component of this plm message, if it has one.  Otherwise it returns null.
	 * @return
	 */
	public InsteonMessage getInsteonMessage() {
		if (code != PlmMessageCode.STANDARD_MESSAGE_RX && code != PlmMessageCode.EXTENDED_MESSAGE_RX) {
			return null;
		}
		
		InsteonAddress fromAddr = new InsteonAddress(data.get(0), data.get(1), data.get(2));
		InsteonAddress toAddr = new InsteonAddress(data.get(3), data.get(4), data.get(5));
		MessageType flags = InsteonMessage.MessageType.valueOfFlag(data.get(6));

		if (code == PlmMessageCode.STANDARD_MESSAGE_RX) {
			return new InsteonMessage(InsteonMessage.MessageLength.STANDARD,fromAddr, toAddr, data.get(7), data.get(8),flags, null);			
		}
		else if (code == PlmMessageCode.EXTENDED_MESSAGE_RX) {
			int[] userData = new int[14];
			for (int i = 0; i < 14; i++) {
				userData[i] = data.get(i+8);
			}
			return new InsteonMessage(InsteonMessage.MessageLength.EXTENDED,fromAddr, toAddr, data.get(7), data.get(8),flags, userData);			
		} else {
			return null;
		}
	}
}
